package com.lube.handler;

import java.net.InetSocketAddress;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.lube.codec.CommandType;
import com.lube.codec.Message;
import com.lube.entities.Sender;
import com.lube.protobuf.ConnectAckProtos.ConnectAck;
import com.lube.protobuf.ConnectProtos.Connect;
import com.lube.support.ChannelManager;
import com.lube.support.ConnectAckCode;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;

/**
 * 业务处理器
 * 
 * @author caizhiping
 * @date 2017年1月24日 下午2:20:37
 */
public class ServerHandler extends SimpleChannelInboundHandler<Message> {

	public final static AttributeKey<Sender> SENDER = AttributeKey.valueOf("sender");

	private static final Logger LOG = LogManager.getLogger(ServerHandler.class);
	
	private int pingTimes;

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, Message msg) throws Exception {
		try {
			pingTimes = 0;
			
			CommandType commandType = msg.getCommandType();
			Channel channel = ctx.channel();

			switch (commandType) {

			case CONNECT:
				doLogin(channel, (Connect) msg.getPayload());
				return;

			case PING:
				LOG.info(commandType);
				channel.writeAndFlush(new Message(CommandType.PINGACK, 0, null));
				return;

			case PUBLISH:

			case PUBACK:

			case DISCONNECT:

			default:
				break;
			}
		} catch (Throwable e) {
			LOG.error(e);
		}

	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		LOG.error("连接异常,关闭连接", cause);
		ctx.channel().close();
	}

	private void doLogin(Channel channel, Connect connect) {
		String clientId = connect.getClientId();
		String username = connect.getUserName();
		String password = connect.getPassword();

		InetSocketAddress inetAddress = (InetSocketAddress) channel.remoteAddress();
		String cliIp = inetAddress.getAddress().getHostAddress();

		LOG.info("登录请求clientIp:{}, clientId:{} ,username:{} ,password:{}", cliIp, clientId, username, password);

		// 把相关信息注入到当前channel
		Sender sender = new Sender();
		sender.setIp(cliIp);
		sender.setClientId(clientId);
		Attribute<Sender> attr = channel.attr(SENDER);
		attr.set(sender);

		// 假设帐号、密码、clientId验证通过(for test)

		ChannelManager.add(clientId, channel);

		ConnectAck connAck = ConnectAck.newBuilder().setReturnCode(ConnectAckCode.LOGIN_SUCCESS).setUserName(username)
				.build();
		Message msg = new Message(CommandType.CONNECTACK, connAck.toByteArray().length, connAck);
		channel.writeAndFlush(msg);
	}

	/**
	 * 利用读空闲检查心跳消息
	 */
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent e = (IdleStateEvent) evt;
			switch (e.state()) {
			case READER_IDLE:
				if (pingTimes >= 3) {
					ctx.close();

					LOG.warn("客户端长期没有信息发送到服务端，关闭连接,pingTime=" + pingTimes);

					pingTimes = 0;
				}else {
					pingTimes ++;
				}
				break;

			default:
				break;
			}
		}
	}
}
